package concurrent.lock;

import lombok.extern.slf4j.Slf4j;
import org.junit.Test;

import java.util.Queue;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.AbstractQueuedSynchronizer;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

/**
 * @program JavaBase
 * @description: 自定义实现的不可重入锁
 *  AQS为什么使用双向队列(双向链表)？
 *    - 前出后入，前驱节点出队O(1)，后驱节点入队O(1)，空间换时间
 *    - 单向链表，前驱节点出队O(1)，只能前驱节点入队O(n)，高并发场景性能相对较差
 * @author: zhanglu
 * @create: 2021-05-22 18:39:00
 */
@Slf4j
public class LuNonReentrantLock implements Lock {

    private final Sync sync = new Sync();

    @Override
    public void lock() {
        sync.acquire(1);
    }

    @Override
    public void lockInterruptibly() throws InterruptedException {
        sync.acquireInterruptibly(1);
    }

    @Override
    public boolean tryLock() {
        return sync.tryAcquire(1);
    }

    @Override
    public boolean tryLock(long timeout, TimeUnit unit) throws InterruptedException {
        return sync.tryAcquireNanos(1, unit.toNanos(timeout));
    }

    @Override
    public void unlock() {
        sync.release(1);
    }

    @Override
    public Condition newCondition() {
        return sync.newCondition();
    }

    private static class Sync extends AbstractQueuedSynchronizer{

        /**
         * 是否锁已经被持有
         * @return
         */
        protected boolean isHeldExclusively(){
            return getState() == 1;
        }

        /**
         * 尝试获取锁
         * @param acquires
         * @return
         */
        public boolean tryAcquire(int acquires){
            assert acquires == 1;
            if(compareAndSetState(0, 1)){
                setExclusiveOwnerThread(Thread.currentThread());
                return true;
            }
            return false;
        }

        public boolean tryRelease(int releases) {
            assert releases == 1;
            if(getState() == 0){
                throw new IllegalMonitorStateException();
            }
            setExclusiveOwnerThread(null);
            setState(0);
            return true;
        }

        /**
         * 提供条件变量接口
         * @return
         */
        public Condition newCondition() {
            return new ConditionObject();
        }

    }

    @Test
    public void test01() throws InterruptedException {
        LuNonReentrantLock lock = new LuNonReentrantLock();
        Condition notFull = lock.newCondition();
        Condition notEmpty = lock.newCondition();
        Queue<String> queue = new LinkedBlockingQueue<>();
        int queueSize = 3;
        //生产者
        Thread t1 = new Thread(() -> {
            while (true) {
                lock.lock();
                try {
                    while (queue.size() == queueSize) {
                        log.info("生产者await");
                        notEmpty.await();
                    }
                    log.info("生产者++");
                    TimeUnit.SECONDS.sleep(1);
                    queue.add("item");
                    notFull.signalAll();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                } finally {
                    lock.unlock();
                }
            }
        });
        //消费者
        Thread t2 = new Thread(() -> {
            while (true) {
                lock.lock();
                try {
                    while (queue.size() == 0) {
                        log.info("消费者await");
                        notFull.await();
                    }
                    log.info("消费者--");
                    String poll = queue.poll();
                    notEmpty.signalAll();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                } finally {
                    lock.unlock();
                }
            }
        });
        t2.start();
        TimeUnit.SECONDS.sleep(1);
        t1.start();

        TimeUnit.SECONDS.sleep(5);
    }

    @Test
    public void test03() throws InterruptedException {
        ReentrantLock lock = new ReentrantLock();
        Condition notFull = lock.newCondition();
        Condition notEmpty = lock.newCondition();
        Queue<String> queue = new LinkedBlockingQueue<>();
        int queueSize = 3;
        //生产者
        Thread t1 = new Thread(() -> {
            lock.lock();
            try {
                while (queue.size() == queueSize) {
                    log.info("生产者await");
                    notEmpty.await();
                }
                log.info("生产者++");
                queue.add("item");
                notFull.signalAll();
            } catch (InterruptedException e) {
                e.printStackTrace();
            } finally {
                lock.unlock();
            }
        });
        //消费者
        Thread t2 = new Thread(() -> {
            lock.lock();
            try {
                while (queue.size() == 0) {
                    log.info("消费者await");
                    notFull.await();
                }
                log.info("消费者--");
                String poll = queue.poll();
                notEmpty.signalAll();
            } catch (InterruptedException e) {
                e.printStackTrace();
            } finally {
                lock.unlock();
            }
        });
        t1.setName("A");
        t2.setName("B");
        t1.start();
        t2.start();
        TimeUnit.SECONDS.sleep(8);
    }

    @Test
    public void test04() throws InterruptedException {
        ReentrantLock lock = new ReentrantLock();
        Condition notFull = lock.newCondition();
        Condition notEmpty = lock.newCondition();
        Queue<String> queue = new LinkedBlockingQueue<>();
        int queueSize = 3;
        //生产者
        Thread t1 = new Thread(() -> {
            while (true) {
                lock.lock();
                try {
                    while (queue.size() == queueSize) {
                        log.info("生产者await");
                        notEmpty.await();
                    }
                    log.info("生产者++");
                    TimeUnit.SECONDS.sleep(1);
                    queue.add("item");
                    notFull.signalAll();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                } finally {
                    lock.unlock();
                }
            }
        });
        //消费者
        Thread t2 = new Thread(() -> {
            while (true) {
                lock.lock();
                try {
                    while (queue.size() == 0) {
                        log.info("消费者await");
                        notFull.await();
                    }
                    log.info("消费者--");
                    String poll = queue.poll();
                    notEmpty.signalAll();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                } finally {
                    lock.unlock();
                }
            }
        });
        t1.setName("A");
        t2.setName("B");
        t2.start();
        TimeUnit.SECONDS.sleep(1);
        t1.start();

        TimeUnit.SECONDS.sleep(8);
    }

}
